﻿using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

namespace Jinndev {

    /// <summary>
    /// 对象池管理器
    /// </summary>
    public class PoolManager : MonoBehaviour {

        #region static
        public static PoolManager Instance { get; private set; }
        public static PoolManager Create(string path) {
            return CommonUtil.Instantiate<PoolManager>(path);
        }
        #endregion

        public bool Initialized { get; private set; }

        private List<PoolGroup> groups = new List<PoolGroup>();

        private void Awake() {
            Instance = this;
            RefreshPools();
        }

        private void OnDestroy() {
            Instance = null;
            groups.Clear();
            groups = null;
        }


        public void Init() {
            foreach (PoolGroup group in groups) {
                group.CreateInit();
            }
            Initialized = true;
        }

        //public System.Action[] GetInitActions() {
        //    List<System.Action> list = new List<System.Action>();
        //    foreach (PoolGroup group in groups) {
        //        list.AddRange(group.GetCreateInitActions());
        //    }
        //    return list.ToArray();
        //}

        public void RefreshPools() {
            groups.Clear();
            groups.AddRange(GetComponentsInChildren<PoolGroup>());
        }

        /// <summary>
        /// 尝试回收物体，如无法回收则直接销毁
        /// </summary>
        /// <param name="poolName"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        public bool Recycle(string poolName, GameObject obj) {
            if (!Initialized) {
                Debug.Log("Not Initialized");
                return false;
            }
            foreach (PoolGroup group in groups) {
                if (group.Match(poolName)) {
                    group.Recycle(obj);
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 添加一个对象池
        /// </summary>
        public PoolGroup AddPoolGroup(string poolName, GameObject prefab, int init = 0, int max = 10, int increment = 1, float destroyInterval = 3f) {
            GameObject obj = new GameObject(poolName);
            obj.transform.SetParent(transform, false);

            PoolGroup poolGroup = obj.AddComponent<PoolGroup>();
            poolGroup.Init(poolName, prefab, init, max, increment, destroyInterval);
            groups.Add(poolGroup);
            return poolGroup;
        }

        /// <summary>
        /// 移除空白对象池
        /// </summary>
        public void ClearEmptyPool() {
            if (Application.isEditor) {
                for (int i = transform.childCount - 1; i >= 0; i--) {
                    Transform child = transform.GetChild(i);
                    PoolGroup[] groups = child.GetComponents<PoolGroup>();
                    foreach (PoolGroup group in groups) {
                        if (group.prefab == null) {
                            DestroyImmediate(group);
                        }
                    }
                    if(child.GetComponents<PoolGroup>().Length == 0) {
                        DestroyImmediate(child.gameObject);
                    }
                }
            }
            else {
                if (!Initialized) {
                    Debug.Log("Not Initialized");
                    return;
                }
                for (int i = groups.Count - 1; i >= 0; i--) {
                    if (groups[i].prefab == null) {
                        Destroy(groups[i].gameObject);
                        groups.RemoveAt(i);
                    }
                }
            }
        }

        /// <summary>
        /// 移除指定对象池
        /// </summary>
        public bool RemovePoolGroup(string poolName) {
            if (!Initialized) {
                Debug.Log("Not Initialized");
                return false;
            }
            for (int i = 0; i < groups.Count; i++) {
                if (groups[i].Match(poolName)) {
                    Destroy(groups[i].gameObject);
                    groups.RemoveAt(i);
                    return true;
                }
            }
            return false;
        }

        public PoolGroup GetMatchPool(GameObject prefab) {
            if (Application.isEditor) {
                PoolGroup[] groups = GetComponentsInChildren<PoolGroup>(true);
                foreach (PoolGroup group in groups) {
                    if (group.Match(prefab)) {
                        return group;
                    }
                }
            }
            else {
                if (!Initialized) {
                    Debug.Log("Not Initialized");
                    return null;
                }
                foreach (PoolGroup group in groups) {
                    if (group.Match(prefab)) {
                        return group;
                    }
                }
            }
            return null;
        }

        public PoolGroup GetMatchPool(string poolName) {
            if (Application.isEditor) {
                PoolGroup[] groups = GetComponentsInChildren<PoolGroup>(true);
                foreach (PoolGroup group in groups) {
                    if (group.Match(poolName)) {
                        return group;
                    }
                }
            }
            else {
                if (!Initialized) {
                    Debug.Log("Not Initialized");
                    return null;
                }
                foreach (PoolGroup group in groups) {
                    if (group.Match(poolName)) {
                        return group;
                    }
                }
            }
            return null;
        }

        /// <summary>
        /// 根据对象池名称创建实例
        /// </summary>
        /// <param name="poolName"></param>
        /// <param name="parent"></param>
        /// <param name="worldPositionStays"></param>
        /// <returns></returns>
        public GameObject Instantiate(string poolName, Transform parent = null, bool worldPositionStays = false) {
            if (!Initialized) {
                Debug.Log("Not Initialized");
                return null;
            }
            PoolGroup group = GetMatchPool(poolName);
            if(group != null) {
                return group.Instantiate(parent, worldPositionStays);
            }
            Debug.LogError($"Pool group prefab not found \"{poolName}\"");
            return null;
        }

        /// <summary>
        /// 根据预设创建实例
        /// </summary>
        /// <param name="prefab"></param>
        /// <param name="parent"></param>
        /// <param name="worldPositionStays"></param>
        /// <returns></returns>
        public GameObject Instantiate(GameObject prefab, Transform parent = null, bool worldPositionStays = false) {
            if (!Initialized) {
                Debug.Log("Not Initialized");
                return null;
            }

            PoolGroup group = GetMatchPool(prefab);
            if (group != null) {
                return group.Instantiate(parent, worldPositionStays);
            }
            Debug.LogError($"Pool group prefab not found \"{prefab.name}\", instantiate prefab directly");
            return prefab == null ? null : Object.Instantiate(prefab, parent, worldPositionStays);
        }

    }

#if UNITY_EDITOR
    [CustomEditor(typeof(PoolManager), true)]
    public class PoolManagerEditor : Editor {

        private PoolManager poolManager;

        private void OnEnable() {
            poolManager = target as PoolManager;
        }

        public override void OnInspectorGUI() {
            DrawDefaultInspector();

            GameObject dropObj = EditorGUILayout.ObjectField("通过预设创建对象池", null, typeof(GameObject), false) as GameObject;
            if(dropObj != null) {
                PoolGroup group = poolManager.GetMatchPool(dropObj);
                if(group == null) {
                    group = poolManager.AddPoolGroup(dropObj.name, dropObj);
                    group.SetupByPrefab();
                }
                Selection.activeObject = group;
            }

            GUILayout.BeginHorizontal();
            if (GUILayout.Button("增加空白对象池")) {
                PoolGroup group = poolManager.AddPoolGroup("NewPool", null);
                Selection.activeObject = group;
            }
            if (GUILayout.Button("删除空白对象池")) {
                poolManager.ClearEmptyPool();
            }
            GUILayout.EndHorizontal();
        }

    }
#endif

}